#pragma once

#include <list>

namespace mediator {
    
/// @brief 抽象同事类, 定义同事类的接口，保存中介者对象，提供同事对象交互的抽象方法，实现所有相互影响的同事类的公共功能
class AbstractMediator;
class AbstractColleague {
public:
    virtual ~AbstractColleague() {}

    /// @brief 抽象接口
    virtual void Receive() = 0;
    virtual void Send() = 0;

    /// @brief 设置中介对象
    void SetMedium(AbstractMediator* mediator) {
        mediator_ = mediator;
    }

protected:
    AbstractMediator* mediator_ = nullptr;
};

/// @brief 具体同事类, 抽象同事类的实现者，当需要与其他同事对象交互时，由中介者对象负责后续的交互
class ConcreteColleagueA : public AbstractColleague {
public:
    virtual void Receive();
    virtual void Send();
};
class ConcreteColleagueB : public AbstractColleague {
public:
    virtual void Receive();
    virtual void Send();
};

/// @brief 抽象中介类, 中介者的接口，提供了同事对象注册与转发同事对象信息的抽象方法
class AbstractMediator {
public:
    virtual ~AbstractMediator() {}

    /// @brief 注册
    virtual void Register(AbstractColleague* colleague) = 0;
    /// @brief 转发
    virtual void Relay(AbstractColleague* colleague) = 0;
};

/// @brief 具体中介类, 实现中介者接口. 管理同事对象，协调各个同事角色之间的交互关系，依赖于同事角色
class ConcreteMediator : public AbstractMediator {
public:
    virtual void Register(AbstractColleague* colleague) {
        for (auto* c : colleagues_) {
            if (c == colleague) return;
        }
        colleagues_.emplace_back(colleague);
        colleague->SetMedium(this);
    }

    virtual void Relay(AbstractColleague* colleague) {
        for (auto* c : colleagues_) {
            // 转发给除自己外的同事
            if (c != colleague) c->Receive();
        }
    }

private:
    std::list<AbstractColleague*> colleagues_;
};

///////////////////////////////////////////////////////// 同事类实现
void ConcreteColleagueA::Receive() {
    printf("this is ConcreteColleagueA::Receive!\n");
}
void ConcreteColleagueA::Send() {
    printf("this is ConcreteColleagueA::Send!\n");
    // 请中介者转发
    mediator_->Relay(this);
}
void ConcreteColleagueB::Receive() {
    printf("this is ConcreteColleagueB::Receive!\n");
}
void ConcreteColleagueB::Send() {
    printf("this is ConcreteColleagueB::Send!\n");
    // 请中介者转发
    mediator_->Relay(this);
}

} // namespace mediator

void Test_19_mediator_impl_1() {
    printf("-------------------- %s --------------------\n", __FUNCTION__);
    std::shared_ptr<mediator::AbstractMediator> ptr_md(new mediator::ConcreteMediator());

    std::shared_ptr<mediator::AbstractColleague> ptr_c1(new mediator::ConcreteColleagueA());
    std::shared_ptr<mediator::AbstractColleague> ptr_c2(new mediator::ConcreteColleagueB());

    ptr_md->Register(ptr_c1.get());
    ptr_md->Register(ptr_c2.get());

    ptr_c1->Send();

    printf("-----------\n");

    ptr_c2->Send();
}

// -------------------- Test_19_mediator_impl_1 --------------------
// this is ConcreteColleagueA::Send!
// this is ConcreteColleagueB::Receive!
// -----------
// this is ConcreteColleagueB::Send!
// this is ConcreteColleagueA::Receive!